package com.dhcc.bpm.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * ZipUtils
 *
 * @author xioafayu
 * @version v1.0
 * @date 20210524
 */
public class ZipUtils {
    private static final Logger log = LoggerFactory.getLogger(ZipUtils.class);
    private static final int BUFFER_SIZE = 2 * 1024;

    /**
     * 压缩成ZIP 方法1
     *
     * @param srcDir           压缩文件夹路径
     * @param outDir           压缩文件输出目录
     * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
     *                         <p>
     *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
     * @throws RuntimeException 压缩失败会抛出运行时异常
     */
    public static void toZip(String srcDir, String outDir, boolean KeepDirStructure)
            throws RuntimeException {
        long start = System.currentTimeMillis();
        ZipOutputStream zos = null;

        try {
            zos = new ZipOutputStream(new FileOutputStream(new File(outDir)));
            File sourceFile = new File(srcDir);
            compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure);

            long end = System.currentTimeMillis();
            System.out.println("压缩完成，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            throw new RuntimeException("zip error from ZipUtils", e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    log.error("异常信息：{}", e);
                }
            }
        }
    }


    /**
     * 压缩成ZIP 方法2
     *
     * @param srcFiles 需要压缩的文件列表
     * @param out      压缩文件输出目录
     * @throws RuntimeException 压缩失败会抛出运行时异常
     */
    public static void toZip(List<File> srcFiles, String out) throws RuntimeException {
        long start = System.currentTimeMillis();
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(new FileOutputStream(new File(out)));
            for (File srcFile : srcFiles) {
                byte[] buf = new byte[BUFFER_SIZE];
                zos.putNextEntry(new ZipEntry(srcFile.getName()));

                int len;
                FileInputStream in = new FileInputStream(srcFile);
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                zos.closeEntry();
                in.close();

            }
            long end = System.currentTimeMillis();
            System.out.println("压缩完成，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            throw new RuntimeException("zip error from ZipUtils", e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    log.error("异常信息：{}", e);
                }
            }
        }
    }

    /**
     * 压缩成ZIP 方法3
     *
     * @param srcFiles     需要压缩的文件列表   保留源目录
     * @param out          压缩文件输出目录
     * @param baseFilePath 根路径
     * @throws RuntimeException 压缩失败会抛出运行时异常
     * @author lizeyu
     */
    public static void toZipByPath(List<File> srcFiles, String out, String baseFilePath) throws RuntimeException {
        long start = System.currentTimeMillis();
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(new FileOutputStream(new File(out)));
            for (File srcFile : srcFiles) {
                byte[] buf = new byte[BUFFER_SIZE];
                String filePath = srcFile.getPath();
                String path = srcFile.getPath();
                log.info("初始路径为:{}", filePath);
                // 绝对路径
                if (filePath.indexOf(":") == 1) {
                    filePath = filePath.replace("\\", "/");
                } else if (filePath.indexOf("\\") == 0 || filePath.indexOf("/") == 0) {
                    filePath = filePath.substring(1);
                    filePath = filePath.replace("\\", "/");
                    filePath = filePath.replace("file/", "");
                    if (baseFilePath.lastIndexOf("/") != baseFilePath.length() - 1 && filePath.indexOf("/") != 0) {
                        filePath = baseFilePath + "/" + filePath;
                    } else {
                        filePath = baseFilePath + filePath;
                    }
                } else {
                    filePath = filePath.replace("\\", "/");
                    filePath = filePath.replace("file/", "");
                    if (baseFilePath.lastIndexOf("/") != baseFilePath.length() - 1 && filePath.indexOf("/") != 0) {
                        filePath = baseFilePath + "/" + filePath;
                    } else {
                        filePath = baseFilePath + filePath;
                    }
                }

                log.info("需要压缩文件的路径为:{}", filePath);
                if (!(new File(filePath).exists())) {
                    continue;
                }
                path = path.replaceAll("//", "/");
//                path = path.replaceAll("\\\\", "/");
//                path = path.replaceAll("\\", "/");
                if (path.indexOf("/") == 0) {
                    path = path.substring(1);
                } else if (filePath.indexOf(":") == 1) {
                    path = filePath.substring(3);
                }
                zos.putNextEntry(new ZipEntry(path));
                int len;
                FileInputStream in = new FileInputStream(new File(filePath));
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                zos.closeEntry();
                in.close();

            }
            long end = System.currentTimeMillis();
            System.out.println("压缩完成，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            log.error("文件压缩失败:{}", e);
//            System.out.println(e);
//            throw new RuntimeException("zip error from ZipUtils", e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    log.error("异常信息：{}", e);
                }
            }
        }
    }

    /**
     * 递归压缩方法
     *
     * @param sourceFile       源文件
     * @param zos              zip输出流
     * @param name             压缩后的名称
     * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
     *                         <p>
     *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
     * @throws Exception
     */
    private static void compress(File sourceFile, ZipOutputStream zos, String name,
                                 boolean KeepDirStructure) throws Exception {
        byte[] buf = new byte[BUFFER_SIZE];
        if (sourceFile.isFile()) {
            // 向zip输出流中添加一个zip实体，构造器中name为zip实体的文件的名字
            zos.putNextEntry(new ZipEntry(name));

            // copy文件到zip输出流中
            int len;
            FileInputStream in = new FileInputStream(sourceFile);
            while ((len = in.read(buf)) != -1) {
                zos.write(buf, 0, len);
            }

            // Complete the entry
            zos.closeEntry();
            in.close();
        } else {
            File[] listFiles = sourceFile.listFiles();
            if (listFiles == null || listFiles.length == 0) {
                // 需要保留原来的文件结构时,需要对空文件夹进行处理
                if (KeepDirStructure) {
                    // 空文件夹的处理
                    zos.putNextEntry(new ZipEntry(name + "/"));
                    // 没有文件，不需要文件的copy
                    zos.closeEntry();
                }
            } else {
                for (File file : listFiles) {
                    // 判断是否需要保留原来的文件结构
                    if (KeepDirStructure) {
                        // 注意：file.getName()前面需要带上父文件夹的名字加一斜杠,
                        // 不然最后压缩包中就不能保留原来的文件结构,即：所有文件都跑到压缩包根目录下了
                        compress(file, zos, name + "/" + file.getName(), KeepDirStructure);
                    } else {
                        compress(file, zos, file.getName(), KeepDirStructure);
                    }
                }
            }
        }
    }


    /**
     * 递归删除
     * 删除某个目录及目录下的所有子目录和文件
     *
     * @param file 文件或目录
     * @return 删除结果
     */
    public static boolean delFiles(File file) {
        boolean result = false;
        //目录
        if (file.isDirectory()) {
            File[] childrenFiles = file.listFiles();
            for (File childFile : childrenFiles) {
                result = delFiles(childFile);
                if (!result) {
                    return result;
                }
            }
        }
        //删除 文件、空目录
        result = file.delete();
        return result;
    }


    private static void moveTotherFolders(String startPath, String endPath, String ansPath, String fileName) {
        try {
            File startFile = new File(startPath);
            File tmpFile = new File(endPath);//获取文件夹路径
            if (!tmpFile.exists()) {//判断文件夹是否创建，没有创建则创建新文件夹
                tmpFile.mkdirs();
            }

            if (startFile.renameTo(new File(endPath + fileName))) {
                System.out.println("File is moved successful!");
                log.info("文件移动成功！文件名：《{}》 目标路径：{}", fileName, endPath);
            } else {
                System.out.println("File is failed to move!");
                log.info("文件移动失败！文件名：《{}》 起始路径：{}", fileName, startPath);
            }
        } catch (Exception e) {
            log.info("文件移动异常！文件名：《{}》 起始路径：{}", fileName, startPath);

        }
    }

    //文件夹的拷贝
    public static void copyDir(String sourcePathDir, String newPathDir) {
        File start = new File(sourcePathDir);
        File end = new File(newPathDir);
        String[] filePath = start.list();//获取该文件夹下的所有文件以及目录的名字
        if (!end.exists()) {
            end.mkdir();
        }
        boolean flag = false;
        for (String temp : filePath) {
            //添加满足情况的条件
            if (new File(sourcePathDir + File.separator + temp).isFile()) {
                //为文件则进行拷贝
                flag = copyFile(sourcePathDir + File.separator + temp, newPathDir + File.separator + temp);

                if (flag) {
                    log.info("文件:{},复制成功！", temp);
                } else {
                    log.info("文件:{},复制失败！", temp);
                }
            }

        }
    }

    //文件的拷贝
    public static boolean copyFile(String sourcePath, String newPath) {
        boolean flag = false;
        File readfile = new File(sourcePath);
        File newFile = new File(newPath);
        try (
                FileOutputStream fileOutputStream = new FileOutputStream(newFile, true);
                Writer writer = new OutputStreamWriter(fileOutputStream, "UTF-8");
                BufferedWriter bufferedWriter = new BufferedWriter(writer);
                BufferedReader bufferedReader = new BufferedReader(new FileReader(readfile));
        ) {
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                bufferedWriter.write(line);
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }
            flag = true;
        } catch (IOException e) {
            flag = false;
            log.error("文件复制异常：{}", e);
        }

        return flag;
    }

    /**
     * 解压到指定目录
     */
    public static void unZipFiles(String zipPath,String descDir)throws IOException
    {
        unZipFiles(new File(zipPath), descDir);
    }
    /**
     * 解压文件到指定目录
     */
    @SuppressWarnings("rawtypes")
    public static void unZipFiles(File zipFile,String descDir)throws IOException
    {
        File pathFile = new File(descDir);
        if(!pathFile.exists())
        {
            pathFile.mkdirs();
        }
        //解决zip文件中有中文目录或者中文文件
        ZipFile zip = new ZipFile(zipFile, Charset.forName("GBK"));
        for(Enumeration entries = zip.entries(); entries.hasMoreElements();)
        {
            ZipEntry entry = (ZipEntry)entries.nextElement();
            String zipEntryName = entry.getName();
            InputStream in = zip.getInputStream(entry);
            String outPath = (descDir+zipEntryName).replaceAll("\\*", "/");
            System.out.println(outPath);
            //判断路径是否存在,不存在则创建文件路径
            File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
            if(!file.exists())
            {
                file.mkdirs();
            }
            //判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
            if(new File(outPath).isDirectory())
            {
                continue;
            }
            //输出文件路径信息

            OutputStream out = new FileOutputStream(outPath);
            byte[] buf1 = new byte[1024];
            int len;
            while((len=in.read(buf1))>0)
            {
                out.write(buf1,0,len);
            }
            in.close();
            out.close();
        }
        log.info("******************解压完毕********************");
    }
}